//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_UNION_TYPE_H
#define _UXS_UNION_TYPE_H

#pragma once

#include "../../stl_core/ux_stl_core.h"

namespace UX_STL
{
namespace UXTypesExt
{
	using namespace UXTypes;


	//
	// Union Type
	//
	
	template <typename TTypeList>
	struct TUnion
	{
	// types
	public:
		typedef TTypeList				TypeList_t;
		typedef TUnion< TTypeList >		Self;
		
	private:
		static const usize	_SIZE = TypeList_t::MaxSizeOf::value;

		typedef char	_data_t[ _SIZE ];

		typedef void (*_Destructor_t) (void *data);
		

	// variables
	private:
		_data_t			_data;
		_Destructor_t	_destructor;
		TypeId_t		_currentTypeId;
		usize			_currentIndex;


	// methods
	public:
		TUnion () :
			_destructor(null),
			_currentIndex(-1)
		{
			_ClearData();
		}
		
		template <typename T>
		TUnion (const T &value) :
			_destructor(null),
			_currentIndex(-1)
		{
			Create( value );
		}

		~TUnion ()
		{
			_Destroy();
		}


		TypeId_t  GetCurrentTypeId () const
		{
			return _currentTypeId;
		}


		usize GetCurrentIndex () const
		{
			return _currentIndex;
		}


		// Type Access //
		template <typename T>
		Self &  Create (const T &value = T())
		{
			STATIC_ASSERT( (TypeList_t::template HasType<T>::value), "type not presented in union" );

			_Destroy();

			_currentIndex	= TypeList_t::template IndexOf<T>::value;
			_currentTypeId	= TypeId<T>();
			_destructor		= & _Destructor<T>;
			
			placement_new( _data, value );

			return *this;
		}


		template <typename T>
		bool IsCurrent () const
		{
			STATIC_ASSERT( (TypeList_t::template HasType<T>::value), "type not presented in union" );
			return TypeId<T>() == _currentTypeId;
		}


		template <typename T>
		T &  Get ()
		{
			STATIC_ASSERT( (TypeList_t::template HasType<T>::value), "type not presented in union" );
			CHECK( TypeId<T>() == _currentTypeId );
			return *PointerCast< T >( _data );
		}


		template <typename T>
		T const &  Get () const
		{
			STATIC_ASSERT( (TypeList_t::template HasType<T>::value), "type not presented in union" );
			CHECK( TypeId<T>() == _currentTypeId );
			return *PointerCast< T >( _data );
		}


		template <typename T>
		T &  Cast ()
		{
			STATIC_ASSERT( (TypeList_t::template HasType<T>::value), "type not presented in union" );
			return *PointerCast< T >( _data );
		}


		template <typename T>
		T const &  Cast () const
		{
			STATIC_ASSERT( (TypeList_t::template HasType<T>::value), "type not presented in union" );
			return *PointerCast< T >( _data );
		}


		// Indexed Access //
		template <usize Index>
		Self &  Create ()
		{
			return Create< typename TypeList_t::Get< Index >::type >();
		}

		template <usize Index>
		bool IsCurrent () const
		{
			return Index <= _currentIndex;
		}

		template <usize Index>
		typename TypeList_t::Get< Index >::type &  Get ()
		{
			return Get< typename TypeList_t::Get< Index >::type >();
		}

		template <usize Index>
		typename TypeList_t::Get< Index >::type const &  Get () const
		{
			return Get< typename TypeList_t::Get< Index >::type >();
		}

		template <usize Index>
		typename TypeList_t::Get< Index >::type &  Cast ()
		{
			return Cast< typename TypeList_t::Get< Index >::type >();
		}

		template <usize Index>
		typename TypeList_t::Get< Index >::type const &  Cast () const
		{
			return Cast< typename TypeList_t::Get< Index >::type >();
		}


	private:
		void _Destroy ()
		{
			_currentTypeId = TypeId_t();

			if ( _destructor != null )
			{
				_destructor( _data );
				_destructor = null;
				
				_ClearData();
			}
		}

		void _ClearData ()
		{
			ZeroMem( _data, _SIZE );
		}

		template <typename T>
		static void _Destructor (void * data)
		{
			((T *) data)->~T();
		}
	};
			
	
}	// UXTypesExt
}	// UX_STL

#endif	// _UXS_UNION_TYPE_H